package br.com.loja.util.reflection;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @brief Classe MethodEntity
 * @author David Luiz
 * @date 04/12/2014
 */
public class JBean {

    private final Object target;
    private String attribute;

    private Object value;

    public JBean(Object target) {
        this.target = target;
    }

    public JBean(Object target, String attribute) {
        this(target);
        this.attribute = attribute;
    }

    /**
     * @return the value
     */
    public Object getValue() {
        this.value = get(target, attribute);
        return value;
    }

    /**
     * @param value the value to set
     */
    public void setValue(Object value) {
        Method method;
        String methodName;
        Object objTarget = target;

        if (attribute == null) {
            throw new IllegalArgumentException("attribute is null");
        }

        String attributes[] = attribute.split("\\.");
        String att = attributes[attributes.length - 1];

        if (attributes.length > 1) {

            String attributeName = attributes[0];
            int length = attributes.length - 1;

            for (int i = 1; i < length; i++) {
                attributeName = attributeName.concat(".").concat(attributes[i]);
            }

            objTarget = get(target, attributeName);

        }

        if (objTarget == null) {
            return;
        }

        Field field = ReflectionUtil.getFieldByName(objTarget.getClass(), att);

        if (field == null) {
            throw new NoSuchFieldError("Atributo não encontrado: " + objTarget.getClass().getSimpleName().toLowerCase() + "." + att);
        }

        methodName = ReflectionUtil.getMethodNameByField(field, ReflectionUtil.SET_METHOD);
        Class<?> paramType = field.getType();

        method = ReflectionUtil.getMethodByName(objTarget.getClass(), methodName, paramType);

        if (method == null) {
            throw new NoSuchMethodError("Método não encontrado: " + objTarget.getClass().getSimpleName().toLowerCase() + "." + methodName);
        }

        try {
            method.invoke(objTarget, value);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
            //
            System.err.println(ex.getMessage());
            ex.printStackTrace();
        }

    }

    private Object get(Object target, String attribute) {

        if (attribute == null) {
            throw new IllegalArgumentException("atributo nulo");
        }

        if (target == null) {
            return null;
        }

        Method method;
        String methodName;
        String attributes[] = attribute.split("\\.", 2);
        Object result = null;

        if (attributes.length > 0) {

            String attributeName = attributes[0];
            Field field = ReflectionUtil.getFieldByName(target.getClass(), attributeName);

            if (field == null) {
                throw new NoSuchFieldError("Atributo não encontrado: " + target.getClass().getSimpleName().toLowerCase() + "." + attributeName);
            }

            Class<?> type = field.getType();

            methodName = ReflectionUtil.getMethodNameByField(field, ReflectionUtil.GET_METHOD);

            method = ReflectionUtil.getMethodByName(target.getClass(), methodName);

            if (method == null) {
                throw new NoSuchMethodError("Método não encontrado: " + target.getClass().getSimpleName().toLowerCase() + "." + methodName);
            }

            try {
                result = type.cast(method.invoke(target));
            } catch (InvocationTargetException e) {
                //implementar log
                System.err.println(e.getTargetException().getMessage());
                e.getTargetException().printStackTrace();
            } catch (IllegalAccessException | IllegalArgumentException ex) {
                //implementar log
                System.err.println(ex.getMessage());
                ex.printStackTrace();
            }

            if ((attributes.length == 2)) {
                result = get(result, attributes[1]);
            }

        }

        return result;
    }

    public void setAttribute(String attribute) {
        this.attribute = attribute;
    }

}
